<!--
  Initial Jinja2 template for generating browser-based reports for SiliconCompiler job runs.

  Currently, the generated page contains two tabular options for displaying the recorded metrics.
  There is also a placeholder for an interactive signoff checklist, but the 'schema_checklist'
  method does not yet contain concrete signoff criteria in the demo ASIC flows.
-->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>SiliconCompiler Manifest Viewer</title>

    <style>
      {% include 'report/bootstrap.min.css' %}
    </style>

    <style>
      /* Styles related to manifest tree view.
      Source: https://stackoverflow.com/a/36297446
      */

      ul.tree li {
          list-style-type: none;
          position: relative;
      }

      ul.tree li ul {
          display: none;
      }

      ul.tree li.open > ul {
          display: block;
      }

      ul.tree li a {
          color: black;
          text-decoration: none;
      }

      ul.tree li a:before {
          height: 1em;
          padding:0 .1em;
          font-size: .8em;
          display: block;
          position: absolute;
          left: -1.3em;
          top: .2em;
      }

      ul.tree li > a:not(:last-child):before {
          content: '+';
      }

      ul.tree li.open > a:not(:last-child):before {
          content: '-';
      }
    </style>

    <script type="text/javascript">
      // Helper method to download the current manifest as a .json file.
      // Creates a virtual 'a' tag and sends it a click event to download the data as a file.
      const saveTemplateAsFile = (filename, dataObjToWrite) => {
        const blob = new Blob([JSON.stringify(dataObjToWrite, null, 4)], { type: "text/json" });
        const link = document.createElement("a");

        link.download = filename;
        link.href = window.URL.createObjectURL(blob);
        link.dataset.downloadurl = ["text/json", link.download, link.href].join(":");

        const evt = new MouseEvent("click", {
          view: window,
          bubbles: true,
          cancelable: true,
        });

        link.dispatchEvent(evt);
        link.remove()
      };

      // Initialize the JSON manifest. Use the 'pruned' version for signoff purposes.
      //var cur_manifest = {{ manifest|tojson|safe }};
      var cur_manifest = {{ pruned_cfg|tojson|safe }};

      // Helpers to make indexing into global value less painful
      function getVal(cfg) {
        if ('global' in cfg['node'] &&
            'global' in cfg['node']['global'] &&
            'value' in cfg ['node']['global']['global']) {
          return cfg['node']['global']['global']['value'];
        } else {
          return cfg['defvalue'];
        }
      }

      function setVal(cfg, val) {
        cfg['node']['global']['global']['value'] = val;
      }

      // Recursive helper for manifest generation.
      function generateManifestBranch(cfg, keypath, parent_list_el) {
        // Iterate over all elements in the given JSON dict.
        // TODO: Sort keys?
        for (const el in cfg) {
         if (!('shorthelp' in cfg[el])) {
            // Branch value.
            new_child = document.createElement("li");
            new_child.innerHTML = '<a href="#">' + el + '</a>'
            new_child_list = document.createElement("ul");
            new_child.appendChild(new_child_list);
            parent_list_el.appendChild(new_child);
            generateManifestBranch(cfg[el], (keypath.concat(el)), new_child_list);
          }
          else if ('node' in cfg[el]) {
            // Leaf value.
            new_child = document.createElement("li");
            new_child.classList.add("open");
            new_child.innerHTML = '<a href="#">' + el + '</a>'
            new_child_list = document.createElement("ul");
            shorthelp_li = document.createElement("li");
            shorthelp_li.innerHTML = cfg[el]['shorthelp'];
            new_child_list.appendChild(shorthelp_li);

            var has_val = false;
            if (keypath.indexOf('checklist') > -1 && el == 'ok') {
              // Provide a checkbox for acceptance of the checklist item.
              ok_li = document.createElement("li");
              ok_id = 'ok_' + keypath.join('_');
              checked = '';
              if (getVal(cfg[el]) && (['false', 'False'].indexOf(getVal(cfg[el])) <= -1)) {
                checked = 'checked';
              }
              ok_li.innerHTML = '<b>Item Accepted:</b> <input id="' + ok_id + '" type="checkbox" ' + checked + '/>';
              new_child_list.appendChild(ok_li);
              has_val = true;
            }
            // TODO: Add a checklist parameter that facilitates free-text entry.
            else {
              // Come up with list of values to possibly display
              var vals = [];
              var has_global = false;
              for (var step in cfg[el]['node']) {
                for (var index in cfg[el]['node'][step]) {
                  if (step == 'global' && index == 'global')
                    has_global = true;

                  if (!('value' in cfg[el]['node'][step][index]))
                    continue;

                  var val = cfg[el]['node'][step][index]['value'];
                  vals.push({'value': val, 'step': step, 'index': index});
                }
              }
              // add default value if no global set
              if (!has_global && cfg[el]['pernode'] != 'required')
                vals.push({'value': cfg[el]['defvalue'], 'step': 'global', 'index': 'global'});

              for (var i=0; i<vals.length; i++) {
                var val = vals[i]['value'];
                var step = vals[i]['step'];
                var index = vals[i]['index'];

                // Don't display value if it doesn't exist, if it's null, or if it's an empty list.
                if (val == null || (Array.isArray(val) && val.length == 0))
                  continue;

                val_li = document.createElement("li");
                var node = '';
                if (step != 'global') {
                  node += ' (' + step;
                  if (index != 'global')
                    node += index
                  node += ')'
                }
                val_li.innerHTML  = '<b>Value' + node + ':</b> ' + val;
                new_child_list.appendChild(val_li);
                has_val = true;
              }
            }
            if (has_val) {
              new_child.appendChild(new_child_list);
              parent_list_el.appendChild(new_child);
            }
          }
        }
      };

      // Helper method to generate a manifest tree-view from a JSON object.
      function generateManifestView(cfg) {
        // Remove the existing tree view elements.
        top_ul = document.getElementById("manifest_ul_top");
        top_ul.replaceChildren();

        // Recursively add li/ul elements to the tree.
        generateManifestBranch(cfg, [], top_ul);

        // Foldable manifest tree logic.
        // Source: https://stackoverflow.com/a/36297446.
        var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
        for(var i = 0; i < tree.length; i++){
            tree[i].addEventListener('click', function(e) {
                var parent = e.target.parentElement;
                var classList = parent.classList;
                if(classList.contains("open")) {
                    classList.remove('open');
                    var opensubs = parent.querySelectorAll(':scope .open');
                    for(var i = 0; i < opensubs.length; i++){
                        opensubs[i].classList.remove('open');
                    }
                } else {
                    classList.add('open');
                }
            });
        }
      };

      // Initialization function to setup buttons/links/etc after all DOM elements are lodaed.
      document.addEventListener("DOMContentLoaded", function() {
        // Fill in the initial manifest view.
        generateManifestView(cur_manifest);

        // Setup the 'download manifest' button.
        var download_btn = document.getElementById("download_manifest_btn");
        download_btn.addEventListener('click', () => {
          // Update the 'cur_manifest' value with any checklist items which might have changed.
          // TODO: A recursive macro like the tree view could also work, but the checklists are shallow.
          {% for checklist_name in manifest['checklist'].keys() %}
            {% if checklist_name != 'default' %}
              {% for item_name in manifest['checklist'][checklist_name].keys() %}
                {% if item_name != 'default' %}
                  var ok_el = document.getElementById("ok_checklist_{{ checklist_name }}_{{ item_name }}");
                  var rpt_el = document.getElementById("report_checklist_{{ checklist_name }}_{{ item_name }}");
                  if (ok_el.checked) {
                    setVal(cur_manifest['checklist']['{{ checklist_name }}']['{{ item_name }}']['ok'], "true")
                  }
                  else {
                    setVal(cur_manifest['checklist']['{{ checklist_name }}']['{{ item_name }}']['ok'], "false");
                  }
                  // TODO: Optional free-text entry.
                  //cur_manifest['checklist']['{{ checklist_name }}']['{{ item_name }}']['report']['value'] = [rpt_el.value];
                {% endif %}
              {% endfor %}
            {% endif %}
          {% endfor %}

          // Send the updated JSON manifest back to the client as a "download" from RAM to disk.
          saveTemplateAsFile('{{ design }}.json', cur_manifest);
        });

        // Setup the 'upload manifest' button listener.
        // TODO: Better error-checking for manifest correctness?
        var upload_btn = document.getElementById("upload_manifest_btn");
        upload_btn.addEventListener('click', () => {
          var manifest_file_in = document.getElementById("manifest_upload_input");
          if (manifest_file_in.files.length != 1) { return; }
          var new_manifest_filereader = new FileReader();
          new_manifest_filereader.readAsText(manifest_file_in.files[0]);
          new_manifest_filereader.onload = function(event) {
            var manifest_txt = event.target.result;
            console.log(manifest_txt);
            var new_manifest = JSON.parse(manifest_txt);
            generateManifestView(new_manifest);
          };
        });

        // Setup links in the metrics tables. Each metric is associated with at least one log file.
        {% for metric in metric_keys %}
          {% for step, index in nodes %}
            {% if reports[step, index][metric] %}
              var sim_log_btn = document.getElementById("{{ step }}_{{ index }}_{{ metric }}_metlink");
              sim_log_btn.addEventListener('click', () => {
                log_link = "{{ step }}/{{ index }}/{{ reports[step, index][metric][0] }}";
                window.open(log_link, "_blank");
              });
              var sim_log_btn = document.getElementById("{{ step }}_{{ index }}_{{ metric }}_ddmetlink");
              sim_log_btn.addEventListener('click', () => {
                log_link = "{{ step }}/{{ index }}/{{ reports[step, index][metric][0] }}";
                window.open(log_link, "_blank");
              });
            {% endif %}
          {% endfor %}
        {% endfor %}
      });
    </script>
  </head>

  <body>
    <script>
      {% include 'report/bootstrap.min.js' %}
    </script>

    <div style="text-align: center; width: 100%;">
      <h1>Design Summary: "{{ design }}"</h1>
    </div>

    <span style="width: 33%; float: left;">
      {% if img_data %}
        <div style="text-align: center; width: 100%;">
          <h2>GDS Preview</h2>
          <img src="data:image/jpeg;base64,{{img_data}}" class="rounded mx-auto d-block" style="width: 100%;"></img>
        </div>
      {% endif %}

      <div id="reviewer_name_entry_div" style="text-align: center;">
        <b>Reviewer Name/ID: </b>
        <br>
        <input id="reviewer_name_entry" type="text">
      </div>

      <div id="cur_manifest_upload" style="text-align: center;">
        <input id="manifest_upload_input" type="file"/>
        <button id="upload_manifest_btn" class="btn btn-danger">Load Manifest</button>
      </div>

      <div id="cur_manifest_download" style="text-align: center;">
        <button id="download_manifest_btn" class="btn btn-warning">Save Updated Manifest</button>
      </div>
    </span>

    <span style="width: 67%; float: right; height: 100vh; overflow: scroll;">
      <div style="text-align: center;"><a class="btn btn-primary" data-bs-toggle="collapse" href="#metrics_table_div", role="button", aria-controls="metrics_table_div" style="width: 30%;">
        Toggle Metrics Table
      </a></div>

      <div id="metrics_table_div" class="collapse" style="text-align: center;">
        <h2>Full {{ design }} Metrics</h2>
        <table id="metrics_table" class="table table-dark table-striped table-bordered">
            <tr>
              <th>-</th>
              <th>units</th>
              {% for step, index in nodes %}
                <th>{{ step }}/{{ index }}</th>
              {% endfor %}
            </tr>
            {% for metric in metric_keys %}
              <tr>
                <th>{{ metric }}</th>
                <th>{{ metrics_unit[metric] }}</th>
                {% for step, index in nodes %}
                  {% if errors[step, index] %}
                    <td>(failed)</td>
                  {% else %}
                    {% set value = metrics[step, index][metric] %}
                    {% if value is not none %}
                      {% set valuestr = value %}
                    {% else %}
                      {% set valuestr = "---" %}
                    {% endif %}

                    {% if reports[step, index][metric] %}
                      {% set href_id = [step + index, metric, "metlink"] | join("_") %}
                      <td><a href="#" class="link-success" id="{{ href_id }}">{{ valuestr }}</a></td>
                    {% else %}
                      <td>{{ valuestr }}</td>
                    {% endif %}
                  {% endif %}
                {% endfor %}
              </tr>
            {% endfor %}
        </table>
      </div>

      <div style="text-align: center;"><a class="btn btn-primary" data-bs-toggle="collapse" href="#metrics_dropdowns_div", role="button", aria-controls="metrics_dropdowns_div" style="width:30%;">
        Toggle Metrics Dropdowns
      </a></div>

      <div id="metrics_dropdowns_div" class="collapse" style="text-align: center;">
        <h2>Metrics for {{ design }} Tasks</h2>
        {% for step, index in nodes %}
          <div>
            <a class="btn btn-success" data-bs-toggle="collapse" href="#{{ step }}_{{ index }}_dropdown_div", role="button", aria-controls="{{ step }}_{{ index }}_dropdown_div">
              Toggle {{ step }}/{{ index }} Metrics
            </a>
          </div>
          <div id="{{ step }}_{{ index }}_dropdown_div" class="collapse">
            <table id="{{ step }}_{{ index }}_metrics_table" class="table table-dark table-striped table-bordered">
              <tr>
                {% for metric in metric_keys %}
                  <th>{{ metric }}</th>
                {% endfor %}
              </tr>
              <tr>
                {% for metric in metric_keys %}
                  {% if errors[step, index] %}
                    <td>(failed)</td>
                  {% else %}
                    {% set value = metrics[step, index][metric] %}
                    {% set unit = metrics_unit[metric] %}
                    {% if value is not none %}
                      {% set valuestr = value %}
                    {% else %}
                      {% set valuestr = "---" %}
                      {% set unit = "" %}
                    {% endif %}

                    {% if reports[step, index][metric] %}
                      {% set href_id = [step + index, metric, "ddmetlink"] | join("_") %}
                      <td><a href="#" class="link-success" id="{{ href_id }}">{{ valuestr }}{{ unit }}</a></td>
                    {% else %}
                      <td>{{ valuestr }}{{ unit }}</td>
                    {% endif %}
                  {% endif %}
                {% endfor %}
              </tr>
            </table>
          </div>
        {% endfor %}
      </div>

      <div style="text-align: center;"><a class="btn btn-primary" data-bs-toggle="collapse" href="#manifest_dropdown_div", role="button", aria-controls="manifest_dropdown_div" style="width:30%;">
        Toggle Manifest Dropdown
      </a></div>

      <div id="manifest_dropdown_div" class="collapse" style="text-align: left; padding-left: 3em;">
        <h2>Manifest</h2>
        <ul id="manifest_ul_top" class="tree">
        </ul>
      </div>

    </span>
  </body>

</html>
